package autoz.czt.launching;

import static autoz.czt.launching.LaunchConfigurationAttributes.OUTPUT_FILE_PATH;

import java.util.List;

import net.sourceforge.czt.parser.util.CztError;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;

import autoz.core.UserNotification.MessageType;
import autoz.core.launching.AbstractLaunchConfigurationDelegate;
import autoz.core.markers.MarkerManager;
import autoz.core.problems.UmlProblem;
import autoz.czt.CZTModule;
import autoz.czt.CZTPlugin;
import autoz.czt.uml.UmlDiagram;

public class LaunchConfigurationDelegate extends AbstractLaunchConfigurationDelegate {
	
	IProgressMonitor progressMonitor;
	
	/**
	 * Run the configuration.  Execute the models, pass specification to checker and 
	 * convert any errors to model-specific error messages.
	 */
	public void launch(ILaunchConfiguration configuration, String mode, ILaunch launch, IProgressMonitor progressMonitor) throws CoreException {
		this.progressMonitor = progressMonitor;
		
		CZTModule module = new CZTModule(configuration);
	
		// Execute the model against the EGL source file.
		setSubTask("Parsing and executing FTL", 100);
		
		try {
			module.generateToFile();
		} catch (Exception e) {
			CZTPlugin.logMessage(IStatus.ERROR, "An error occurred whilst executing the EGL source.", e, true);
			return;
		}
		
		// Pass the ZOO specification to CZT
		try {
			String zooSpecificationWorkspacePath = configuration.getAttribute(OUTPUT_FILE_PATH,"");
			IFile iFile = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(zooSpecificationWorkspacePath));
			iFile.refreshLocal(IResource.DEPTH_ZERO, progressMonitor); // refresh the contents of the file.
			String zooSpecificationFullPath = iFile.getLocation().toPortableString();
			CZTEngine cztEngine = new CZTEngine(zooSpecificationFullPath);
			
			// Parse
			setSubTask("Parsing ZOO specification with CZT", 100);
			List<CztError> parseErrors = cztEngine.parse();
			if (parseErrors != null && !parseErrors.isEmpty()){
				String p = "";
				for (CztError e : parseErrors){
					System.out.println("pe:"+e);
					p += e.toString() + "\n";
				}
				CZTPlugin.logMessage(
						IStatus.ERROR, 
						"The Z specification did not parse. This should not happen. This means there is a bug in the templates. Errors:" + p, 
						new Exception("hi"), 
						true);
				return;
			}
			
			// Type check
			setSubTask("Parsing ZOO specification with CZT", 100);
			List<UmlProblem> typecheckErrors = cztEngine.typecheck();
			
			// Map schema errors to model elements
			List<UmlDiagram> umlDiagrams = module.ripOutClassesFromModels(); // Should only really do this if there are errors...

			// If there are any errors, print them to the problems tab
			if (typecheckErrors.size() > 0){
				for (UmlDiagram d : umlDiagrams){
					markDiagramWithErrors(d, module, typecheckErrors);
					d.setDirty(true);
				}
				
				// Inform them of the bad news 
				CZTPlugin.displayUserNotification("Sadly, AUtoZ found problems with your model. Please consult the Problems tab.", MessageType.ERROR);
				
			} else{
				// Clear any markers and let them know they've done a good job. 
				CZTPlugin.displayUserNotification("Well done, AUtoZ found no problems with your models.", MessageType.INFORMATION);
			}
			
			// Errors may have been found in one diagram and not another, therefore we need to 
			// clear markers only on those diagrams which aren't dirty.
			for (UmlDiagram d : umlDiagrams){
				if (!d.isDirty()) MarkerManager.clearErrorMarkers((IFile)d.getDiagram());	
			}
			
		} catch (Exception e) {
			CZTPlugin.logMessage(IStatus.ERROR, "There was an error whilst parsing or typechecking the specification", e, true);
			return;
		}
	}
	
	/**
	 * Marks errors on the file associated with the diagram. 
	 * @param diagram 
	 * @param module 
	 * @param typecheckErrors
	 * @throws Exception
	 */
	private void markDiagramWithErrors(UmlDiagram diagram, CZTModule module, List<UmlProblem> typecheckErrors) throws Exception{
		List<UmlProblem> refactoredTypecheckErrors =  module.assignTypecheckErrorsToModelElements(diagram, typecheckErrors);
		
		MarkerManager markermanager;
		IFile diagramFile = (IFile)diagram.getDiagram();
		if (diagramFile != null && diagramFile.exists()) {
			markermanager = new MarkerManager(diagramFile);
			markermanager.replaceErrorMarkers(refactoredTypecheckErrors);
		} else {
			throw new Exception("Resource was null or didn't exist");
		}
	}
	
	private void setSubTask(String subTask, int totalWork){
		progressMonitor.subTask(subTask);
		progressMonitor.beginTask(subTask, totalWork);
	}
}

